03.03.B - Snake en Python
Lenguajes Estructurados
Objetivo del ejercicio
Crear un programa en Python que simule un tablero de juego de Snake y comparar las estrategias de implementación en relación al programa en C realizado anteriormente. El lenguaje python puede considerarse multi-paradigma, ya que permite programar en forma estructurada, orientada a objetos y funcional. Esto nos permite implementar el juego de Snake con programción estructurada y orientada a objetos, y comparar luego las estrategias de implementación.
Instalación
- Instalación de Pygame: Abrir la terminal y ejecutar el siguiente comando:
pip install pygame
Programación orientada a objetos
Comenzaremos con una implementación orientada a objetos, que es la más común en Python. En este caso, vamos a crear una clase Snake que represente a la serpiente en el juego, y una clase Fruit que represente a la fruta.
- Importación de bibliotecas y configuración de variables:
import pygame
import sys
import random
# Configuración inicial
pygame.init()
clock = pygame.time.Clock()
# Tamaño de la ventana y de los bloques
cell_size = 20
cell_number = 20
screen = pygame.display.set_mode((cell_number * cell_size, cell_number * cell_size))
# Colores
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
- Creación de clases Snake y Fruit
class Snake:
def __init__(self):
self.body = [pygame.Rect(5, 5, cell_size, cell_size)]
self.direction = pygame.Vector2(1, 0)
def draw(self):
for segment in self.body:
pygame.draw.rect(screen, GREEN, segment)
def move(self):
new_head = self.body[0].copy()
new_head.move_ip(self.direction * cell_size)
self.body.insert(0, new_head)
self.body.pop()
class Fruit:
def __init__(self):
self.randomize()
def draw(self):
pygame.draw.rect(screen, RED, self.rect)
def randomize(self):
x = random.randint(0, cell_number - 1) * cell_size
y = random.randint(0, cell_number - 1) * cell_size
self.rect = pygame.Rect(x, y, cell_size, cell_size)
- Creación de instancias de las clases y bucle principal del juego
snake = Snake()
fruit = Fruit()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
snake.direction = pygame.Vector2(0, -1)
if event.key == pygame.K_DOWN:
snake.direction = pygame.Vector2(0, 1)
if event.key == pygame.K_LEFT:
snake.direction = pygame.Vector2(-1, 0)
if event.key == pygame.K_RIGHT:
snake.direction = pygame.Vector2(1, 0)
screen.fill(WHITE)
snake.move()
snake.draw()
fruit.draw()
if snake.body[0].colliderect(fruit.rect):
fruit.randomize()
pygame.display.update()
clock.tick(10)
Este código crea un juego básico de Snake en Python. A continuación, se explica cada paso:
- Instalar e importar Pygame y otras bibliotecas necesarias.
- Configurar el tamaño de la ventana, los colores y las variables iniciales.
- Crear las clases Snake y Fruit, que representan a la serpiente y a la fruta en el juego. Estas clases tienen métodos para dibujar sus elementos en la pantalla y actualizar sus posiciones.
- Crear instancias de las clases Snake y Fruit, e inicializar el bucle principal del juego. El bucle principal maneja eventos como el cierre de la ventana y las entradas del teclado. Además, controla el movimiento y la detección de colisiones entre la serpiente y la fruta.
Mejoras
Ahora se puede mejorar el juego agregando algunas funciones adicionales, como el crecimiento de la serpiente al comer la fruta y la detección de colisiones con las paredes o con su propio cuerpo.
Mejorar el método move de la clase Snake y agregar la función check_collisions:
class Snake:
# ...
def move(self):
new_head = self.body[0].copy()
new_head.move_ip(self.direction * cell_size)
self.body.insert(0, new_head)
if new_head.colliderect(fruit.rect):
fruit.randomize()
else:
self.body.pop()
def check_collisions(self):
head = self.body[0]
if head.x < 0 or head.y < 0 or head.x >= cell_number * cell_size or head.y >= cell_number * cell_size:
return True
for segment in self.body[1:]:
if head.colliderect(segment):
return True
return False
Mejorar el bucle principal para incluir la detección de colisiones y reiniciar el juego si es necesario:
# ...
while True:
for event in pygame.event.get():
# ... (event handling)
screen.fill(WHITE)
snake.move()
snake.draw()
fruit.draw()
if snake.check_collisions():
snake = Snake() # Reiniciar la serpiente
pygame.display.update()
clock.tick(10)
Con estos cambios, ahora el juego detecta si la serpiente choca contra las paredes o contra sí misma y reinicia la serpiente si es necesario. Además, la serpiente crecerá al comer la fruta en lugar de simplemente moverse.
Ahora tenemos un juego de Snake básico pero funcional en Python con Pygame. Se puede continuar mejorando y personalizando el juego agregando más características, como marcadores, niveles de dificultad, animaciones y sonidos.
Programacion Estructurada
En esta versión, debemos eliminar las clases y en su lugar hay que usar funciones y variables globales para gestionar la lógica del juego. Las funciones draw_rect, move_snake, randomize_fruit y check_collisions realizan las tareas que antes eran parte de las clases Snake y Fruit. El bucle principal sigue siendo similar, pero ahora llama a estas funciones en lugar de utilizar métodos de las clases.
Algunas ideas para mejorar el juego:
Agregar un sistema de puntuación: llevar la cuenta de la puntuación del jugador, que podría ser la longitud de la serpiente o la cantidad de frutas consumidas, y mostrarla en la pantalla usando Pygame.
Aumentar la velocidad del juego con el tiempo: hacer que el juego sea más desafiante incrementando la velocidad a medida que la serpiente crece o según la puntuación del jugador.
- Agregar niveles de dificultad: implementar diferentes niveles de dificultad modificando factores como el tamaño de la celda, la velocidad inicial o las condiciones del juego (por ejemplo, obstáculos en la pantalla).
- Agregar sonidos y animaciones: agregar sonidos y animaciones para mejorar la experiencia del jugador. Por ejemplo, se podría agregar un sonido cuando la serpiente come una fruta, o una animación cuando la serpiente choca contra una pared o contra su propio cuerpo.
PyGame
Pygame es una biblioteca de Python que se basa en Simple DirectMedia Layer (SDL), una biblioteca de desarrollo de videojuegos multiplataforma escrita en C. SDL proporciona acceso de bajo nivel a elementos como audio, teclado, ratón y gráficos a través de OpenGL y Direct3D.
Pygame actúa como un "wrapper" o envoltorio alrededor de SDL, lo que significa que utiliza y se comunica con SDL para manejar gráficos, audio, eventos de entrada y otras funcionalidades relacionadas con el desarrollo de videojuegos y aplicaciones multimedia.
Al utilizar Pygame, los desarrolladores de Python pueden aprovechar las capacidades de SDL a través de una interfaz más simple y orientada a objetos, específicamente diseñada para Python.
La combinación de SDL y Pygame permite a los desarrolladores de Python crear juegos y aplicaciones multimedia de una manera más rápida y fácil, sin tener que lidiar con la complejidad y las particularidades de las APIs de bajo nivel y la programación en C.
Funciones específicas utilizadas
move_ip
La función move_ip es un método de la clase Rect en la biblioteca Pygame. Esta función se utiliza para mover un rectángulo en su lugar, cambiando sus atributos x e y por un desplazamiento dado en las coordenadas (x, y). La función move_ip toma dos argumentos, dx y dy, que representan el desplazamiento en el eje x y en el eje y, respectivamente. El sufijo _ip en el nombre del método indica que se trata de una operación "in place", lo que significa que modifica el objeto rectángulo directamente en lugar de devolver un nuevo rectángulo.
En el ejemplo del juego Snake, usamos move_ip para actualizar la posición de la cabeza de la serpiente en función de su dirección actual. Aquí hay un fragmento del código donde se utiliza move_ip:
new_head = snake[0].copy()
new_head.move_ip(direction * cell_size)
En este fragmento, primero copiamos el rectángulo que representa la cabeza de la serpiente (snake[0]) y luego aplicamos move_ip en la copia new_head. El desplazamiento en x e y se calcula multiplicando el vector de dirección por el tamaño de la celda. Finalmente, insertamos new_head en la lista snake como el nuevo primer elemento, que se convierte en la nueva cabeza de la serpiente.
colliderect
La función colliderect es otro método de la clase Rect en la biblioteca Pygame. Este método se utiliza para determinar si dos rectángulos se están superponiendo (colisionando) o no. La función colliderect toma un único argumento, que es otro objeto Rect, y devuelve True si ambos rectángulos tienen una intersección no vacía, es decir, si están colisionando. Si no hay intersección entre los rectángulos, devuelve False.
En el ejemplo del juego Snake, usamos colliderect para verificar si la cabeza de la serpiente ha colisionado con la fruta y, en la función check_collisions, para verificar si la cabeza de la serpiente ha colisionado con su propio cuerpo. Aquí tienes un fragmento de código donde se utiliza colliderect:
if new_head.colliderect(fruit):
fruit.randomize()
En este fragmento, comprobamos si el rectángulo new_head, que representa la nueva posición de la cabeza de la serpiente, colisiona con el rectángulo fruit.rect, que representa la fruta. Si colliderect devuelve True, significa que la serpiente ha comido la fruta, y llamamos a la función randomize para reposicionar la fruta en una nueva ubicación aleatoria.